/*
 * Copyright (c) 2020-2021, Bluetrum Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2021/10/21     AlgoIdeas    The first version
 */

#include <rtthread.h>
#include <board.h>
#include <string.h>
#include <stdio.h>
#include <time.h>

#include "tinygps.h"
#include "gps.h"

#define GPS_UART_NAME          "uart1"
#define GPS_USING_MUTEX         0

/* 用于接收消息的信号量 */
static struct rt_semaphore rx_sem;
static rt_device_t gps_serial;
static char located_flag = 0;
static unsigned long fix_age = 0;
static GPS_Info_t gps_info;
static rt_mutex_t gps_mutex = RT_NULL;
static gps_callback_t gps_callback = RT_NULL;


static void gps_update_time(rt_uint32_t year, rt_uint32_t month, rt_uint32_t day,
                rt_uint32_t hour, rt_uint32_t minute, rt_uint32_t second)
{
    rt_err_t ret = RT_EOK;
    time_t now;

    ret = set_date(year, month, day);
    if (ret != RT_EOK)
    {
        rt_kprintf("set RTC date failed\n");
        return ;
    }

    ret = set_time(hour, minute, second);
    if (ret != RT_EOK)
    {
        rt_kprintf("set RTC time failed\n");
        return ;
    }

    return ;
}

static rt_err_t gps_uart_input(rt_device_t dev, rt_size_t size)
{
     /* 串口接收到数据后产生中断，调用此回调函数，然后发送接收信号量 */
     rt_sem_release(&rx_sem);
     return RT_EOK;
}

static void gps_serial_thread_entry(void *parameter)
{
    char ch;

    while (1)
    {
        /* 从串口读取一个字节的数据，没有读取到则等待接收信号量 */
        while (rt_device_read(gps_serial, -1, &ch, 1) != 1)
        {
            rt_sem_take(&rx_sem, RT_WAITING_FOREVER);
        }

#if GPS_USING_MUTEX
        rt_mutex_take(gps_mutex, RT_WAITING_FOREVER);
#endif

        gps_info.isVaild = gps_encode(ch);
        if (gps_info.isVaild) {
            gps_f_get_position(&gps_info.latitude, &gps_info.longitude, &fix_age);
            gps_info.satellites = gps_satellites();
            gps_crack_datetime(&gps_info.clock.year, &gps_info.clock.month, &gps_info.clock.day,
               &gps_info.clock.hour, &gps_info.clock.minute, &gps_info.clock.second,
               &gps_info.clock.millisecond, &fix_age);

            /* Sync Time */
            if ((0 == located_flag) && (gps_info.clock.year >= 2021)) {
                gps_update_time(gps_info.clock.year, gps_info.clock.month, gps_info.clock.day,
                       gps_info.clock.hour + 8, gps_info.clock.minute, gps_info.clock.second);
                located_flag = 1;
            }

            gps_info.altitude = gps_f_altitude();
            gps_info.speed = gps_f_speed_kmph();
            gps_info.course = gps_f_course();

            if (RT_NULL != gps_callback) {
                gps_callback(gps_info);
            }
        } else {
            if (RT_NULL != gps_callback) {
                gps_callback(gps_info);
            }
        }

#if GPS_USING_MUTEX
        rt_mutex_release(gps_mutex);
#endif
    }
}

int gps_init(gps_callback_t callback)
{
    rt_err_t ret = RT_EOK;
    char uart_name[RT_NAME_MAX];
    rt_strncpy(uart_name, GPS_UART_NAME, RT_NAME_MAX);

    if (RT_NULL != callback) {
        gps_callback = callback;
    }

#if GPS_USING_MUTEX
    gps_mutex = rt_mutex_create("gps_mutex", RT_IPC_FLAG_FIFO);
#endif

    /* 查找系统中的串口设备 */
    gps_serial = rt_device_find(uart_name);
    if (!gps_serial)
    {
        rt_kprintf("find %s failed!\n", uart_name);
        return RT_ERROR;
    }

    rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);

    /* 以中断接收及轮询发送模式打开串口设备 */
    rt_device_open(gps_serial, RT_DEVICE_FLAG_INT_RX);
    /* 设置接收回调函数 */
    rt_device_set_rx_indicate(gps_serial, gps_uart_input);

    rt_thread_t thread = rt_thread_create("gsp_serial", gps_serial_thread_entry, RT_NULL, 4096, 25, 10);
    if (thread != RT_NULL)
    {
        rt_thread_startup(thread);
    }
    else
    {
        ret = RT_ERROR;
    }

    return ret;
}

bool gps_get_info(GPS_Info_t* info)
{
#if GPS_USING_MUTEX
    rt_mutex_take(gps_mutex, RT_WAITING_FOREVER);
#endif

    info->isVaild = gps_info.isVaild;
    info->longitude = gps_info.longitude;
    info->latitude = gps_info.latitude;
    info->altitude = gps_info.altitude;
    info->speed = gps_info.speed;
    info->course = gps_info.course;

    info->clock.year = gps_info.clock.year;
    info->clock.month = gps_info.clock.month;
    info->clock.day = gps_info.clock.day;
    info->clock.hour = gps_info.clock.hour;
    info->clock.minute = gps_info.clock.minute;
    info->clock.second = gps_info.clock.second;
    info->satellites = gps_info.satellites;

#if GPS_USING_MUTEX
    rt_mutex_release(gps_mutex);
#endif

    return info->isVaild;
}

float gps_get_distance(GPS_Info_t* info,  float preLong, float preLat)
{
    return gps_distance_between(info->latitude, info->longitude, preLat, preLong);
}
